#include "jionotifier_p.h"
#include <JXQCore/JMutex>
#include <memory>
#include <map>

namespace JXQ
{

JIONotifierPrivate::JIONotifierPrivate(JIONotifier *q_ptr, int descriptor, Type type) :
	m_descriptor(descriptor), m_type(type)
{
	auto &listener = ListenThread::instance();
	listener.addDescriptor(q_ptr, descriptor, type);
	listener.start();
}

JIONotifierPrivate::~JIONotifierPrivate()
{
	ListenThread::instance().deleteDescriptor(m_descriptor);
	close();
}

/*--------------------------------------------------------------------------------------------*/

JIONotifier::JIONotifier(int descriptor, Type type) :
	d_ptr(new JIONotifierPrivate(this, descriptor, type))
{

}

JIONotifier::~JIONotifier()
{
	delete d_ptr;
}

int JIONotifier::descriptor() const
{
	return d_ptr->m_descriptor;
}

Type JIONotifier::type() const
{
	return d_ptr->m_type;
}

void JIONotifier::setCloseOnDelete(bool enable)
{
	d_ptr->m_closeOnDelete = enable;
}

bool JIONotifier::closeOnDelete() const
{
	return d_ptr->m_closeOnDelete;
}

void JIONotifier::event(Type)
{

}

/*--------------------------------------------------------------------------------------------*/

struct J_DECL_HIDDEN JIONotifierPair
{
	JIONotifier *notifier;
	bool isExtern;
};

class J_DECL_HIDDEN JMultiIONotifierPrivate
{
public:
	JMultiIONotifierPrivate(JMultiIONotifier *q_ptr);
	void registerDescriptor(JIONotifier *notifier, bool isExtern);

public:
	JMultiIONotifier *q_ptr;
	std::mutex m_mutex;

	bool m_deleteOnUnregister = true;
	std::map<int, JIONotifierPair>  m_notifierMap;
};

inline JMultiIONotifierPrivate::JMultiIONotifierPrivate(JMultiIONotifier *q_ptr) :
	q_ptr(q_ptr)
{

}

void JMultiIONotifierPrivate::registerDescriptor(JIONotifier *notifier, bool isExtern)
{
	J_MUTEX_LOCKER(m_mutex);
	if( m_notifierMap.emplace(notifier->descriptor(), JIONotifierPair{notifier, isExtern}).second )
	{
		notifier->ready.subscribe(q_ptr, &JMultiIONotifier::event);
		notifier->ready.subscribe(&q_ptr->ready, &JMessage<int,Type>::send);
	}
}

/*--------------------------------------------------------------------------------------------*/

JMultiIONotifier::JMultiIONotifier() :
	d_ptr(new JMultiIONotifierPrivate(this))
{

}

JMultiIONotifier::~JMultiIONotifier()
{
	unregisterAll();
	delete d_ptr;
}

void JMultiIONotifier::registerDescriptor(JIONotifier *notifier)
{
	d_ptr->registerDescriptor(notifier, true);
}

void JMultiIONotifier::registerDescriptor(int descriptor, JIONotifier::Type type)
{
	d_ptr->registerDescriptor(new JIONotifier(descriptor, type), false);
}

void JMultiIONotifier::unregisterDescriptor(int descriptor)
{
	J_MUTEX_LOCKER(d_ptr->m_mutex);
	auto it = d_ptr->m_notifierMap.find(descriptor);
	if( it == d_ptr->m_notifierMap.end() )
		return ;

	if( not it->second.isExtern or d_ptr->m_deleteOnUnregister )
		delete (*it).second.notifier;
	else
	{
		it->second.notifier->ready.unsubscribe(this, &JMultiIONotifier::event);
		it->second.notifier->ready.unsubscribe(&this->ready, &JMessage<int,Type>::send);
	}

	d_ptr->m_notifierMap.erase(it);
}

void JMultiIONotifier::unregisterDescriptor(JIONotifier *notifier)
{
	unregisterDescriptor(notifier->descriptor());
}

void JMultiIONotifier::unregisterAll()
{
	J_MUTEX_LOCKER(d_ptr->m_mutex);
	auto it = d_ptr->m_notifierMap.begin();

	if( d_ptr->m_deleteOnUnregister )
	{
		while( it != d_ptr->m_notifierMap.end() )
		{
			delete (*it).second.notifier;
			it = d_ptr->m_notifierMap.erase(it);
		}
	}
	else
	{
		while( it != d_ptr->m_notifierMap.end() )
		{
			if( not it->second.isExtern )
				delete (*it).second.notifier;
			else
			{
				it->second.notifier->ready.unsubscribe(this, &JMultiIONotifier::event);
				it->second.notifier->ready.unsubscribe(&this->ready, &JMessage<int,Type>::send);
			}
			it = d_ptr->m_notifierMap.erase(it);
		}
	}
}

void JMultiIONotifier::setDeleteOnUnregister(bool enable)
{
	J_MUTEX_LOCKER(d_ptr->m_mutex);
	d_ptr->m_deleteOnUnregister = enable;
}

bool JMultiIONotifier::deleteOnUnregister() const
{
	return d_ptr->m_deleteOnUnregister;;
}

void JMultiIONotifier::event(int, JIONotifier::Type)
{

}

} //namespace JXQ
